---
id: custombetweenanddatahandlingadapter
title: 模板解析“区间数据”数据适配器
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import BilibiliCard from '@site/src/components/BilibiliCard.js';

### 定义

命名空间：TouchSocket.Core <br/>
程序集：[TouchSocket.Core.dll](https://www.nuget.org/packages/TouchSocket.Core)

## 一、说明

区间适配器，一般用于字符串类的消息，类似“\*\*Hello##”，该数据，以\*\*开头，以##结尾。当然，区间适配器也能用于二进制数据，但是会有概率发生标识重复的情况。所以，用于二进制时，应当设置较复杂的区间标识。

该适配器与[终止因子分割适配器](./packageadapter.mdx)相比，可以设置开头的字符区间。

<BilibiliCard title="用户适配器之区间字符模板适配器" link="https://www.bilibili.com/cheese/play/ep1522752" isPro="true"/>

## 二、特点

1. 可以自由适配**很多**的字符串数据协议。
2. 可以随意定制数据协议。
3. 可以与**任意语言、框架**对接数据。


## 三、使用

客户端与服务器均适用。下列以服务器为例。

步骤

1. 声明新建类，实现**IRequestInfo**接口，此对象即为存储数据的实体类，可在此类中声明一些属性，以备使用。
2. 声明新建类，继承**CustomBetweenAndDataHandlingAdapter**，并且以步骤1声明的类作为泛型。并实现对应抽象方法。
3. TouchSocketConfig配置中设置。
4. 通过Received（事件、方法、插件）中的RequestInfo对象，强转为步骤1声明的类型，然后读取其属性值，以备使用。

【MyBetweenAndRequestInfo】
首先，新建MyBetweenAndRequestInfo类，然后实现**IRequestInfo**接口。

```csharp showLineNumbers
class MyBetweenAndRequestInfo : IRequestInfo
{
    public MyBetweenAndRequestInfo(byte[] body)
    {
        this.Body = body;
    }

    public byte[] Body { get; private set; }
}
```

新建MyCustomBetweenAndDataHandlingAdapter继承**CustomBetweenAndDataHandlingAdapter**，然后对StartCode、EndCode作出赋值，以此表明起始字符与结束字符的值。

```csharp showLineNumbers
class MyCustomBetweenAndDataHandlingAdapter : CustomBetweenAndDataHandlingAdapter<MyBetweenAndRequestInfo>
{
    public MyCustomBetweenAndDataHandlingAdapter()
    {
        this.MinSize = 5;//表示，实际数据体不会小于5，例如“**12##12##”数据，解析后会解析成“12##12”

        this.m_startCode=Encoding.UTF8.GetBytes("**");//可以为0长度字节，意味着没有起始标识。
        this.m_endCode=Encoding.UTF8.GetBytes("##");//必须为有效值。
    }

    private readonly byte[] m_startCode;
    private readonly byte[] m_endCode;

    public override byte[] StartCode => m_startCode;

    public override byte[] EndCode => m_endCode;

    protected override MyBetweenAndRequestInfo GetInstance(ReadOnlySpan<byte> body)
    {
        return new MyBetweenAndRequestInfo(body.ToArray());
    }
}
```

<details>
<summary>旧版本（3.0.14以前）</summary>
<div>

客户端与服务器均适用。下列以服务器为例。

步骤

1. 声明新建类，实现**IBetweenAndRequestInfo**接口，此对象即为存储数据的实体类，可在此类中声明一些属性，以备使用。
2. 声明新建类，继承**CustomBetweenAndDataHandlingAdapter**，并且以步骤1声明的类作为泛型。并实现对应抽象方法。
3. TouchSocketConfig配置中设置。
4. 通过Received（事件、方法、插件）中的RequestInfo对象，强转为步骤1声明的类型，然后读取其属性值，以备使用。

【MyBetweenAndRequestInfo】
首先，新建MyBetweenAndRequestInfo类，然后实现**IBetweenAndRequestInfo**接口。

```csharp showLineNumbers
/// <summary>
/// 以**12##12##，Min=5为例。
/// </summary>
class MyBetweenAndRequestInfo : IBetweenAndRequestInfo
{
    public byte[] Body { get; private set; }

    public void OnParsingBody(ReadOnlySpan<byte> body)
    {
        this.Body = body.ToArray();
    }

    public bool OnParsingEndCode(ReadOnlySpan<byte> endCode)
    {
        return true;//该返回值决定，是否执行Receive
    }

    public bool OnParsingStartCode(ReadOnlySpan<byte> startCode)
    {
        return true;
    }
}
```

新建MyCustomBetweenAndDataHandlingAdapter继承**CustomBetweenAndDataHandlingAdapter**，然后对StartCode、EndCode作出赋值，以此表明起始字符与结束字符的值。

```csharp showLineNumbers
class MyCustomBetweenAndDataHandlingAdapter : CustomBetweenAndDataHandlingAdapter<MyBetweenAndRequestInfo>
{
    public MyCustomBetweenAndDataHandlingAdapter()
    {
        this.MinSize = 5;//表示，实际数据体不会小于5，例如“**12##12##”数据，解析后会解析成“12##12”

        this.m_startCode=Encoding.UTF8.GetBytes("**");//可以为0长度字节，意味着没有起始标识。
        this.m_endCode=Encoding.UTF8.GetBytes("##");//必须为有效值。
    }

    private readonly byte[] m_startCode;
    private readonly byte[] m_endCode;

    public override byte[] StartCode => m_startCode;

    public override byte[] EndCode => m_endCode;

    protected override MyBetweenAndRequestInfo GetInstance()
    {
        return new MyBetweenAndRequestInfo();
    }
}
```

</div>
</details>

【接收】

```csharp showLineNumbers
private static async Task<TcpClient> CreateClient()
{
    var client = new TcpClient();
    //载入配置
    await client.SetupAsync(new TouchSocketConfig()
         .SetRemoteIPHost("127.0.0.1:7789")
         .SetTcpDataHandlingAdapter(() => new MyCustomBetweenAndDataHandlingAdapter())
         .ConfigureContainer(a =>
         {
             a.AddConsoleLogger();//添加一个日志注入
         }));

    await client.ConnectAsync();//调用连接，当连接不成功时，会抛出异常。
    client.Logger.Info("客户端成功连接");
    return client;
}

private static async Task<TcpService> CreateService()
{
    var service = new TcpService();
    service.Received = (client, e) =>
    {
        //从客户端收到信息

        if (e.RequestInfo is MyBetweenAndRequestInfo myRequest)
        {
            client.Logger.Info($"已从{client.Id}接收到：消息={Encoding.UTF8.GetString(myRequest.Body)}");
        }
        return Task.CompletedTask;
    };

    await service.SetupAsync(new TouchSocketConfig()//载入配置
         .SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
         .SetTcpDataHandlingAdapter(() => new MyCustomBetweenAndDataHandlingAdapter())
         .ConfigureContainer(a =>
         {
             a.AddConsoleLogger();//添加一个控制台日志注入（注意：在maui中控制台日志不可用）
         })
         .ConfigurePlugins(a =>
         {
             //a.Add();//此处可以添加插件
         }));
    await service.StartAsync();//启动
    service.Logger.Info("服务器已启动");
    return service;
}
```

:::tip 提示

上述创建的适配器客户端与服务器均适用。

:::  

## 四、适配器纠错

该适配器当收到半包数据时，会自动缓存半包数据，然后等后续数据收到以后执行拼接操作。

但有的时候，可能由于其他原因导致后续数据错乱，这时候就需要纠错。详情可看[适配器纠错](./adaptererrorcorrection.mdx)
